﻿using System;
using System.Collections.Generic;
using System.Threading;

namespace Allegro.TaskExecution
{
    /// <summary>
    /// Context variable
    /// </summary>
    public interface IContextVariable
    {
        ///<summary>
        /// Variable name
        ///</summary>
        string Name { get; }

        ///<summary>
        /// Variable value
        ///</summary>
        object Value { get; }
    }
    /// <summary>
    /// Task execution context
    /// </summary>
    public interface IExecutionContext
    {
        /// <summary>
        /// Get item by variable name
        /// </summary>
        /// <param name="varName"></param>
        /// <returns></returns>
        object this[string varName] { get; set; }

        /// <summary>
        /// Variables in the context
        /// </summary>
        IList<IContextVariable> Variables { get; }

        /// <summary>
        /// True if the variable is set, false otherwise
        /// </summary>
        /// <param name="varName"></param>
        /// <returns></returns>
        bool HasVariable(string varName);

        /// <summary>
        /// Progress aggregator. May be null. In that case, no progress aggregation will be performed
        /// </summary>
        IExecutionProgressAggregator ProgressAggregator { get; set; }

        /// <summary>
        /// The instance with which this context is associated. May be null
        /// </summary>
        object This { get; }

        /// <summary>
        /// Set the progress for a particular task and aggregate the overall progress up to the root task.
        /// This method should be used with caution because it recomputes progress for the entire task tree.
        /// </summary>
        /// <param name="task"></param>
        /// <param name="progress">Progress, between 0 and 1. 1 = complete</param>
        /// <param name="msg"></param>
        void SetProgressAndCompute(ITask task, double progress, string msg);

        /// <summary>
        /// Check if the task execution should be suspended or aborted. If the task has been suspeded,
        /// the method waits until the task resumes. If the task has been aborted, the method throws an exception.
        /// </summary>
        void CheckIfSuspendOrAbort();

        /// <summary>
        /// The outer context, null if this is the root context
        /// </summary>
        IExecutionContext OuterContext { get; }

        /// <summary>
        /// The root context, null if this is a root context
        /// </summary>
        IExecutionContext RootContext { get; }
    }

    ///<summary>
    ///</summary>
    public interface ITaskContextDerivation
    {
        /// <summary>
        /// Fork the currrent context by creating a new context of the same type
        /// with copies of the variables. Any nested and root contexts will be shared between the two contexts
        /// </summary>
        /// <returns></returns>
        IExecutionContext Fork();

        /// <summary>
        /// Create a new context of the same type, nesting the current context. The derived context is
        /// initially empty. The root context of the new context will be the same as the current context.
        /// The "this" variable in the derived context will be set to self. Index is used as lookup key
        /// to retreive the derived context from the DerivedContexts collection.
        /// </summary>
        /// <returns></returns>
        IExecutionContext New(object self);

        /// <summary>
        /// Create a new context of the same type, nesting the current context. The derived context is 
        /// initially empty. The root context of the new context will be the same as the current context.
        /// The "this" variable in the derived context will be set to self. Index is used as lookup key
        /// to retreive the derived context from the DerivedContexts collection.
        /// </summary>
        /// <param name="index"></param>
        /// <param name="self"></param>
        /// <returns></returns>
        IExecutionContext New(object index, object self);

        /// <summary>
        /// The outer context, null if this is the root context
        /// </summary>
        IExecutionContext OuterContext { get; }

        /// <summary>
        /// The root context, null if this is a root context
        /// </summary>
        IExecutionContext RootContext { get; }

        /// <summary>
        /// Contexts derived from this context
        /// </summary>
        IDictionary<object, IExecutionContext> DerivedTaskContexts { get; }

        /// <summary>
        /// Search for the context in the derived context tree below the current context. If no associated 
        /// context is found, return null.
        /// </summary>
        /// <param name="contextKey"></param>
        /// <returns></returns>
        IExecutionContext FindTaskExecutionContext(object contextKey);
    }

    /// <summary>
    /// This interface is must be implemented by contexts in which tasks execute
    /// The tasks coordinate their execution through this interface
    /// </summary>
    public interface ITaskContextCoordination : IExecutionContext
    {
        /// <summary>
        /// Create/reset the task completion event
        /// </summary>
        /// <param name="task"></param>
        void RegisterTaskInContext(ITask task);

        /// <summary>
        /// Wait for the task to complete. The task execution thread blocks until
        /// the task completes by invoking the Complete method
        /// </summary>
        /// <param name="task"></param>
        void WaitOnTaskCompletion(ITask task);

        /// <summary>
        /// Signal that the task has completed in this context. An event is fired that
        /// allows tasks waiting on the task to continue
        /// </summary>
        /// <param name="task"></param>
        void Complete(ITask task);

        /// <summary>
        /// Get the completion event for the task. If no completion event is registered, null
        /// is returned
        /// </summary>
        /// <param name="task"></param>
        /// <returns></returns>
        EventWaitHandle CompletionEvent(ITask task);

        /// <summary>
        /// If the job has been suspended, the method will wait until the task is resumed
        /// </summary>
        void WaitIfJobIsSuspended();

        /// <summary>
        /// If the job has been aborted, the method will throw an exception
        /// </summary>
        void ExitIfJobIsAborted();
    }

    /// <summary>
    /// Context interface for 
    /// </summary>
    public interface ITaskExecutionControl
    {
        /// <summary>
        /// True if execution has been aborted
        /// </summary>
        bool IsAborted { get; set; }

        /// <summary>
        /// Suspend task execution in this context
        /// </summary>
        void SuspendTaskExecution();

        /// <summary>
        /// Resume task execution in this context
        /// </summary>
        void ResumeTaskExecution();

        /// <summary>
        /// Abort task execution in this context
        /// </summary>
        void AbortTaskExecution();

        /// <summary>
        /// Add breakpoint
        /// </summary>
        /// <param name="taskId"></param>
        /// <param name="breakpointId"></param>
        void AddBreakpoint(string taskId, string breakpointId);

        /// <summary>
        /// Remove breakpoint
        /// </summary>
        /// <param name="taskId"></param>
        /// <param name="breakpointId"></param>
        void RemoveBreakpoint(string taskId, string breakpointId);

        /// <summary>
        /// List of breakpoints
        /// </summary>
        IList<string> Breakpoints { get; }

        /// <summary>
        /// Return true if the breakpoint is set
        /// </summary>
        /// <param name="taskId"></param>
        /// <param name="breakpointId"></param>
        /// <returns></returns>
        bool BreakpointIsSet(string taskId, string breakpointId);
    }

    ///<summary>
    ///</summary>
    public interface IExecutionProgressAggregator
    {
        /// <summary>
        /// Reset the aggregator
        /// </summary>
        void Reset();

        /// <summary>
        /// Update the progress for an object. progress is a fraction between 0 and 1
        /// </summary>
        /// <param name="item"></param>
        /// <param name="progress"></param>
        /// <param name="message"></param>
        void UpdateProgress(object item, double progress, string message);

        /// <summary>
        /// Get current progress for the object in the given context
        /// </summary>
        /// <param name="item"></param>
        /// <returns></returns>
        double GetProgress(object item);

        /// <summary>
        /// Latest progress information
        /// </summary>
        string ProgressMessage { get; }

        /// <summary>
        /// Last item that was updated
        /// </summary>
        object LastUpdatedItem { get; }
    }
}
